<?php
/**
 * Created by PhpStorm.
 * User: mawenhuan
 * Date: 2019-06-04
 * Time: 14:08
 */

namespace App\Utils;


use App\Exceptions\HuaxiaException;
use App\Exceptions\LogicException;
use App\Models\Member;
use App\Models\Order;
use App\Models\PayOrderModel;
use App\Models\SysConfig;
use App\Models\Wxinfo;
use App\Models\Wxinfos;
use App\Services\HuaXiaPayService;
use EasyWeChat\Factory;
use Illuminate\Support\Facades\Log;
use function EasyWeChat\Kernel\Support\generate_sign;

class PayUtil
{

    const PAYTYPE_WX = 1;
    const PAYTYPE_ALI = 2;
    const PAYTYPE_WX_MINI = 6;
    const PAYTYPE_HX = 20;

    public static function wxPrepay(PayOrderModel $order)
    {
        $options = config('wechat');
        // 是否为微信小程序
        $is_mini = isset($order->paytype) && $order->paytype === Order::PAYTYPE_WX_MINI;
        if ($is_mini) {
            // 取小程序appid
            $options['payment']['default']['app_id'] = $options['mini_program']['default']['app_id'];
        }
        $payment = Factory::payment($options['payment']['default']);
        $totalFee = $order->totalFee();
        if (env('APP_ENV', 'prod') !== 'prod') {
            $totalFee = 1;
        }
        $data = [
            'trade_type'       => $is_mini ? 'JSAPI' : 'APP',
            'body'             => $order->getBody(),
            'detail'           => $order->getDetail(),
            'out_trade_no'     => $order->getOrderNo(),
            'total_fee'        => $totalFee, // 微信是以分为单位
        ];
        if ($is_mini) {
            $data['openid'] = Wxinfo::fetchOpenIdByMid($order->mid);
        }
        $result = $payment->order->unify($data);

        Log::debug('prepay', [
            'order' => $order->getOrderNo(),
            'data' => $data,
            'result' => $result
        ]);
        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
            if ($is_mini){
                // todo 小程序二次签名的参数必须与下面相同
                $config = [
                    'appId'     => $options['mini_program']['default']['app_id'],
                    'timeStamp' => strval(time()),
                    'nonceStr'  => $result['nonce_str'],
                    'package'   => 'prepay_id=' . $result['prepay_id'],
                    'signType'  => 'MD5'
                ];

                // config('wechat.payment.default.key')为商户的key
                $config['paySign'] = generate_sign($config, config('wechat.payment.default.key'));

            } else {
                $config = [
                    'mch_id' => $result['mch_id'],
                    'nonce_str' => $result['nonce_str'],
                    'appid' => $result['appid'],
                    'sign' => $result['sign'],
                    'prepay_id' => $result['prepay_id']
                ];
            }

        } else {
            Log::debug($result);
            throw new LogicException('微信预支付失败');
        }

        return $config;
    }

    public static function wxMiniPrepay(PayOrderModel $order)
    {
        $options = config('wechat');
        // 取小程序appid
        $options['payment']['default']['app_id'] = $options['mini_program']['default']['app_id'];
        $payment = Factory::payment($options['payment']['default']);
        $totalFee = $order->totalFee();
        if (env('APP_ENV', 'prod') !== 'prod') {
            $totalFee = 1;
        }
        $data = [
            'trade_type'       => 'JSAPI',
            'body'             => $order->getBody(),
            'detail'           => $order->getDetail(),
            'out_trade_no'     => $order->getOrderNo(),
            'total_fee'        => $totalFee, // 微信是以分为单位
        ];
        $wxinfo = Wxinfos::fetchByMid($order->mid);
        $data['openid'] = $wxinfo->openid;
        $result = $payment->order->unify($data);

        Log::debug('prepay', [
            'order' => $order->toArray(),
            'data' => $data,
            'result' => $result
        ]);
        if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
            // todo 小程序二次签名的参数必须与下面相同
            $config = [
                'appId'     => $options['mini_program']['default']['app_id'],
                'timeStamp' => strval(time()),
                'nonceStr'  => $result['nonce_str'],
                'package'   => 'prepay_id=' . $result['prepay_id'],
                'signType'  => 'MD5',
            ];

            // config('wechat.payment.default.key')为商户的key
            $config['paySign'] = generate_sign($config, config('wechat.payment.default.key'));

        } else {
            Log::debug($result);
            throw new LogicException('微信预支付失败');
        }

        return $config;
    }

    public static function huaxiaPrepay(PayOrderModel $order)
    {
//        if ($wxPayInfo = RedisUtil::get(RedisKeys::HUX_XIA_PREPAY_CACHE . $order->getOrderNo())){
//            return json_decode($wxPayInfo, true);
//        }
        $options = config('wechat');
        $wxinfo = Wxinfos::fetchByMid($order->mid);
        $totalFee = $order->totalFee();
        if (env('APP_ENV', 'prod') !== 'prod') {
            $totalFee = 1;
        }
        $config = [
            'orderNo' => $order->getOrderNo(),
            'amount' => round($totalFee, 2),
            'subject' => $order->getBody(),
            'desc' => $order->getDetail(),
            'openId' => $wxinfo->openid,
            'subAppid' => $options['mini_program']['default']['app_id'],
            'expireTime' => 15
        ];
        $huaxiaService = new HuaXiaPayService($config);
        $res = $huaxiaService->run($huaxiaService->config());
        if (!is_array($res)) {
            throw new LogicException('prepay faild result null');
        }
        Log::debug('huaxiaprepay: ', [
            'res' => $res,
            'config' => $huaxiaService->config()
        ]);
        if (empty($res['respCode']) || $res['respCode'] !== '000000' || empty($res['wxPayInfo'])){
            Log::error('huaxiaprepay: ', $res);
            throw new LogicException('prepay faild');
        }
        $order->paytype = PayOrderModel::PAY_TYPE_HUA_XIA;
        $order->save();
        return $res['wxPayInfo'];
    }

    public static function miniPayProxy($order)
    {
        // todo 根据配置与灰度名单选择支付通道
        if (sysconfig::fetchCol('pay_channel') === SysConfig::PAY_CHANNEL_HX){
            return static::huaxiaPrepay($order);
        }
        return static::wxMiniPrepay($order);
    }

    public static function prepay(PayOrderModel $order, $paytype = self::PAYTYPE_WX)
    {
        switch ($paytype) {
            case self::PAYTYPE_WX:
                return static::wxPrepay($order);
            case self::PAYTYPE_WX_MINI:
            case self::PAYTYPE_HX:
                return static::misponsor_midniPayProxy($order);
            default:
                return static::wxPrepay($order);
        }
    }

    /**
     * ---- 根据微信订单号退款
     * @param string $order_no
     * @param string $refund_no
     * @param int $totalFee
     * @param int $refundFee
     * @param array $config
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public static function refund(string $orderNo, string $refundNo, int $totalFee, int $refundFee, array $config = [])
    {
        if (env('APP_ENV', 'prod') !== 'prod') {
            $totalFee = 1;
            $refundFee = 1;
        }
        $options = config('wechat');
        // 取小程序appid
        $options['payment']['default']['app_id'] = $options['mini_program']['default']['app_id'];
        $payment = Factory::payment($options['payment']['default']);
        $result = $payment->refund->byTransactionId($orderNo, $refundNo, $totalFee, $refundFee, $config = []);
        return $result;
    }

    /**
     * ---查询退款情况
     * @param $order_no
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public static function queryRefund($order_no)
    {
        $options = config('wechat');
        // 取小程序appid
        $options['payment']['default']['app_id'] = $options['mini_program']['default']['app_id'];
        $payment = Factory::payment($options['payment']['default']);
        $res = $payment->refund->queryByTransactionId($order_no);
        return $res;
    }
}
